#include "stage.hpp"

#include <string>
#include <algorithm>

#include "data.hpp"

const char PROPCHARS[Stage::PROPCT] =
{
	's', 'u', 'd', 'w', 'f'
};

Stage::Stage()
{
}

Stage::Stage(int c, int r)
{
}

Stage::~Stage()
{
	Close();
}

void Stage::Init(int cols, int rows)
{
	grid.Clear();
	grid.Resize(cols, rows);

	tprops.clear();
}

#include "disk.hpp"

bool Stage::Open(std::string & name)
{
	tileimg = AllImgs.Get(name);
	if (!tileimg)
		return false;

	Data stgdat;

	if (!stgdat.Open(name))
	{
		grid.Resize(40, 30);
		return true;
	}

	int cols = 0, rows = 0;
	Data gtdat;
	Data tpdat;

	for (Data d = stgdat.Begin(); d; ++d)
	{
		if (d.Named("cols"))
			cols = d.Int();
		else if (d.Named("rows"))
			rows = d.Int();
		else if (d.Named("grid"))
			gtdat = d.Begin();
		else if (d.Named("tprops"))
			tpdat = d.Begin();
	}

	grid.Resize(cols, rows);
	
	for (int i = 0;
		gtdat; 
		++i, ++gtdat)
	{
		grid.PutI(i, gtdat.Int());
	}

	for ( ; tpdat; ++tpdat)
	{
		Data tdat;
		Data pdat;

		for (Data d = tpdat.Begin(); d; ++d)
		{
			if (d.Named("tile"))
				tdat = d;
			else if (d.Named("props"))
				pdat = d;
		}

		if (!tdat || !pdat)
			continue;

		int tile = tdat.Int();
		std::string propstr(pdat.CStr());

		for (int p = 0; p < PROPCT; ++p)
		{
			size_t pos = propstr.find(PROPCHARS[p]);
			if (pos != std::string::npos)
				PropSet(tile, p);
		}
	}

	stgdat.Close();

	return true;
}

bool Stage::Save(std::string & name)
{
	Data gdat;
	gdat.InitArray();
	Data prevt;
	for (int i = 0; i < grid.TileCt(); ++i)
	{
		prevt = gdat.InsAfter(prevt, NULL, Data(grid.GetI(i)));
	}

	Data tpdat;
	tpdat.InitArray();
	std::map<int,int>::iterator tprop;
	Data prevtp;
	for (tprop = tprops.begin(); tprop != tprops.end(); ++tprop)
	{
		int tile = tprop->first;
		std::string propstr("");
		
		for(int p = 0; p < PROPCT; ++p)
		{
			if(PropGet(tile, p))
				propstr.push_back(PROPCHARS[p]);
		}
	
		Data tp;
		tp.InitObj();
		tp.InsAfter(Data(), "props", Data(propstr.c_str()));
		tp.InsAfter(Data(), "tile", Data(tile));

		prevtp = tpdat.InsAfter(prevtp, NULL, tp);
	}

	Data cdat(Cols()), rdat(Rows());

	Data stgdat;
	stgdat.InitObj();
	stgdat.InsFront("cols", cdat);
	stgdat.InsAfter(cdat, "rows", rdat);
	stgdat.InsAfter(rdat, "grid", gdat);
	stgdat.InsAfter(gdat, "tprops", tpdat);

	bool saved = stgdat.Save(name);
	stgdat.Close();
	return saved;
}

void Stage::Close()
{
	//tileimg.Close();

	grid.Clear();

	tprops.clear();
}

int Stage::Replace(int c, int r, const Grid & repl)
{
	if (repl.TileCt() == 1)
		return grid.Replace(Get(c, r), repl.Get(0, 0));

	Box2i region (Vec2i(c, r), Vec2i(repl.Cols(), repl.Rows()));

	Grid srch (grid, region);

	return grid.GReplace(srch, repl);
}

bool Stage::PropGet(int tile, int prop)
{
	std::map<int, int>::iterator tprop;
	tprop = tprops.find(tile);

	if (tprop == tprops.end()) 
		return false;

	return tprop->second & PropBit(prop);
}

float Stage::PropArea(int prop, float x1, float y1, float x2, float y2)
{
	float area = 0;

	for (float x = x1; x < (int)x2 + 1; x += 1)
	{
		for (float y = y1; y < (int)y2 + 1; y += 1)
		{
			int tile = Get(x, y);
			if (PropGet(tile, prop))
			{
				Vec2f talt (std::max((float)(int)x, x1),
					std::max((float)(int)y, y1));
				Vec2f tarb (std::min(1.f + (int)x, x2),
					std::min(1.f + (int)y, y2));
				area += (tarb.x - talt.x) * (tarb.y - talt.y);
			}
		}
	}

	return area;
}

int Stage::PropBits(int tile)
{
	std::map<int,int>::iterator it = tprops.find(tile);
	if (it == tprops.end())
	{
		return 0;
	}
	return it->second;
}
//int Stage::PropCt(int prop, const Boxf & b)
//{
//	return PropCt(prop, b.Left(), b.Top(), b.Right(), b.Bottom());
//}
//
//int Stage::PropCt(int prop, int c1, int r1, int c2, int r2)
//{
//	int ct = 0;
//
//	for (int c = c1; c <= c2, ++c)
//	{
//		for (int r = r1; r <= r2; ++r)
//		{
//			int tile = Get(c, r);
//			ct += (PropGet(tile) != 0);
//		}
//	}
//
//	return ct;
//}

void Stage::PropSet(int tile, int prop)
{
	tprops[tile] |= PropBit(prop);
}

void Stage::PropUnset(int tile, int prop)
{
	std::map<int, int>::iterator tprop;
	tprop = tprops.find(tile);

	if (tprop != tprops.end())
	{
		tprops[tile] &= ~PropBit(prop);

		if (!tprop->second)
			tprops.erase(tprop);
	}
}

int Stage::PropsFind(int c1, int r1, int c2, int r2)
{
	int props = 0;

	for (int r = r1; r <= r2; ++r)
	{
		for (int c = c1; c <= c2; ++c)
		{
			int tile = Get(c, r);
			std::map<int,int>::iterator tprop;
			tprop = tprops.find(tile);
			if (tprop != tprops.end())
				props |= tprop->second;
		}
	}

	return props;
}

#include <cfloat>

float Stage::AxisHit(const Box2f & b, const Vec2f & bpos, int ax, int hitprops)
{
	float hit = FLT_MAX;
	Vec2i lefttop (bpos + b.LeftTop());
	Vec2i rightbot (bpos + b.RightBot());

	for (int l = lefttop[ax]; l <= rightbot[ax]; ++l)
	{
		for (int t = lefttop[!ax]; t <= rightbot[!ax]; ++t)
		{
			Vec2i tpos (ax == Y ? Vec2i(t, l) : Vec2i(l, t));
			int tn = grid.Get(tpos.x, tpos.y);

			if (PropBits(tn) & hitprops)
			{
				Vec2f thit (BoxHitVec(tpos.x, tpos.y, b, bpos));

				if (std::abs(thit[ax]) <= std::abs(thit[!ax]))
				{
					if (hit == FLT_MAX)
						hit = 0;
					hit += thit[ax];
					break;
				}
			}
		}
	}

	return hit;
}

Vec2f Stage::BoxHitVec(int c, int r, const Box2f & b, const Vec2f & bpos)
{
	const Box2f tbox (Vec2f(0, 0), Vec2f(1, 1));
	return b.HitVec(tbox, Vec2f(c, r) - bpos);
}

void Stage::Clamp(int & col, int & row)
{
	grid.Clamp(col, row);
}

#include "video.hpp"

void Stage::Draw()
{
	Vid.Pos(0, 0);
	Vid.Pri(-1);
	Vid.Flip(false, false);
	Vid.Color(1, 1, 1, 1);
	Vid.TilesDraw(*tileimg, grid.Data(), Cols(), Rows());
}

Stage Stg;
